const fs = require('fs')
const path = require('path')
const randomWords = require('random-words')

let pagesJson = {}
let pageArr = []
let outerTemplate = {}

function readJson() {
	const pa = `${__dirname}/app`
	const filePath2 = path.join(__dirname, `./app`)
	fs.exists(filePath2, (exists => {
		if (!exists) {
			fs.mkdir(filePath2, (err) => {
				if (err) return
			})
		}
	}))
	// 读取Json
	let filePath = path.join(__dirname, `./app`)
	// 同步读取第一个参数代表文件路径（不可省略)        
	pagesJson = fs.readFileSync(path.join(__dirname, './page.json'), {
		encoding: 'utf8'
	})
	pagesJson = JSON.parse(pagesJson)
	emptyDir(filePath)
	pagesJson.pages.forEach((page, i) => {
		pageArr[i] = {
			num: 0,
			pagesJson
		}
		copyDir(page, i)
	})
}

/**
 * 删除指定目录下所有子文件
 * @param {*} path 
 */
function emptyDir(path) {
	const files = fs.readdirSync(path);

	files.forEach(file => {
		const filePath = `${path}/${file}`;
		const stats = fs.statSync(filePath);
		if (stats.isDirectory()) {
			emptyDir(filePath);
			fs.rmdirSync(filePath)
		} else {
			fs.unlinkSync(filePath);
		}

	});
}

/**
 * 将模板template递归复制到app
 * @param {String} page 当前第几个app包
 * @param {String} index app索引
 */
function copyDir(page, index) {
	let dirName = randomWords()
	let filePath = path.join(__dirname, `./template`)
	let toFilePath = path.join(__dirname, `./app/${dirName}`)
	fs.cp(filePath, toFilePath, {
		recursive: true
	}, (err) => {
		if (err) {
			console.error(err);
			return
		}
		readPagesJson(dirName, index)
		page.forEach(pageItem => {
			let _tip = pageItem[Object.keys(pageItem)[0]].childNode
			let ItemArr = pageItem.content
			outerTemplate[Object.keys(pageItem)[0]] = createFatherNode(Object.keys(pageItem)[0], Object
				.values(pageItem)[0])
			ItemArr.forEach((itemPage, itemIndex) => { // 内容区域
				mkComponentDir(dirName)
				createNvueFile(randomWords(), itemPage, dirName, index, _tip)
			})
		})
	})
}

/**
 * 检测app下是否存在components 不存在就创建
 * @param {String} dirName 包名
 */
function mkComponentDir(dirName) {
	let filePath = path.join(__dirname, `./app/${dirName}/pages/components`)
	fs.exists(filePath, (exists => {
		if (!exists) {
			fs.mkdir(filePath, (err) => {
				if (err) return
			})
		}
	}))
}
/**
 *  创建对应页面的vue文件
 * @param {String} fileName 包名
 * @param {String} page 配置的节点
 * @param {String} dirName 随机文件名
 * @param {String} index 当前的第几个页面
 */
function createNvueFile(fileName, page, dirName, index, _tip) {
	let filePath = path.join(__dirname, `./app/${dirName}/pages/${fileName}`)
	fs.exists(filePath, (exists => {
		if (exists) {
			createPage(`${filePath}/${fileName}.vue`, page, dirName, index, _tip)
		} else {
			fs.mkdir(filePath, (err) => {
				if (err) return
				createPage(`${filePath}/${fileName}.vue`, page, dirName, index, _tip)
			})
		}
	}))
}
/**
 * @param {String} filePath 文件路径
 * @param {String} page 配置的节点
 * @param {String} dirName 随机文件名
 * @param {String} index 当前的第几个页面
 */
function createPage(filePath, page, dirName, index, _tip) {
	console.log('_tip_l', _tip);
	// 加入全局公共参数
	let template = createTemplate(mergeNodes(page, outerTemplate), dirName)
	let components = template.components
	components = components.slice(0, components.length - 1)
	// 每一个页面生成后都要把父节点加入的子节点删除再去生成下一个页面
	outerTemplate.tip.childNode = _tip.slice(0, _tip.length)
	let content = `
	<style>
	   @import '../animate.min.css';
	   @import '../free.css';
	</style>
	<template>
		${template.body}
	</template>
	
	<script>
		${template.importUrl}
		export default {
      components: {
        ${components}
      },
			data() {
				return ${JSON.stringify(template.vueData)}
			}
		}
	</script>
	`
	fs.writeFile(filePath, content, (err) => {
		if (err) return

		pageArr[index].num += 1
		if (pageArr[index].num == pagesJson.pages[index].length) {
			writePageJson(dirName, index)
		}
	})

}

/**
 * 公共节点合并
 * @param {String} page 子节点
 * @param {String} outerTemplate 父节点
 */
function mergeNodes(page, outerTemplate) {
	Object.values(outerTemplate)[0].childNode = [...Object.values(outerTemplate)[0].childNode, ...page]
	return outerTemplate
}

/**
 * @param {String} pageInfo 当前页所有使用到的子组件
 * @param {String} dirName 随机文件名
 */
function createTemplate(pageInfo, dirName) {
	let body = ''
	let vueData = {}
	let importUrl = ``
	let components = ``
	let componentArr = []
	let _newComponentsName = {}
	for (let nodeName in pageInfo) {
		vueData = {
			...vueData,
			...createVueData(nodeName, pageInfo[nodeName])
		}
		_newComponentsName = createNode(nodeName, pageInfo[nodeName])
		body += _newComponentsName._resultName
		componentArr = [...componentArr, ..._newComponentsName._componentsName]
		console.log('componentArr', componentArr);
		for (let _k in pageInfo[nodeName].props) {
			let v = pageInfo[nodeName].props[_k]
			if (typeof v == 'string' && v.indexOf('static/') != -1) {
				copyStaticFile(v, dirName)
			}
		}
	}


	componentArr = new Set(componentArr)
	componentArr = [...componentArr]
	componentArr.forEach(e => {
		importUrl += `import ${e} from '../components/${e}/${e}.vue';`
		components += `${e},`
		copyComponent(e, dirName)
	})
	for (let key in vueData) {
		if (vueData[key] instanceof Array) {
			vueData[key].forEach(e => {
				for (let _k in e) {
					let v = e[_k]
					if (typeof v == 'string' && v.indexOf('static/') != -1) {
						copyStaticFile(v, dirName)
					}
				}
			})
		}
	}
	return {
		body,
		vueData,
		importUrl,
		components
	}
}

/**
 * 递归生成当前页面prop数据放入data
 * @param {String} nodeName 节点名称(key)
 * @param {String} node 节点（value）
 */
function createVueData(nodeName, node) {
	let data = {}
	// node为组件节点
	for (let prop in node.props) {
		if (typeof node.props[prop] != 'string') {
			data[`${nodeName}${titleCase5(prop)}`] = node.props[prop]
		}
	}
	if (node.childNode) {
		for (let i = 0; i < node.childNode.length; i++) {
			for (let childNodeName in node.childNode[i]) {
				data = {
					...data,
					...createVueData(childNodeName, node.childNode[i][childNodeName])
				}
			}
		}
	}
	return data
}

/**
 * 递归修改父节点随机值
 * @param {String} nodeName 父节点名
 * @param {String} fatherPage 父节点值
 */
function createFatherNode(nodeName, node) {
	// 每一次创建子页面的时候加入这些公共参数
	for (let newNodeName in node) {
		if (node.appoint instanceof Array && node.appoint.length > 0) {
			node[newNodeName] = node[newNodeName]
			node.appoint = checkoutAppoint(nodeName, node).numbers
		}
	}
	if (node.childNode) {
		for (let i = 0; i < node.childNode.length; i++) {
			for (let childNodeName in node.childNode[i]) {
				createFatherNode(childNodeName, node.childNode[i][childNodeName])
			}
		}
	}
	return node
}




/**
 * 递归生成当前页面prop数据放入data
 * @param {String} nodeName 节点名称（key）
 * @param {String} node 节点（value）
 */
let nameArr = []

function createNode(nodeName, node) {
	let props = ``
	let name = node.isComponent ? checkoutAppoint(nodeName, node).name : 'view'
	let childNodeStr = ``
	for (let prop in node.props) {
		let type = typeof node.props[prop]
		let propName = `${nodeName}${titleCase5(prop)}`
		let propStr = `${type!='string'?':':''}${prop}="${type!='string'?propName:node.props[prop]}"`
		props += ` ${propStr}`
	}
	if (node.childNode) {
		for (let i = 0; i < node.childNode.length; i++) {
			for (let childNodeName in node.childNode[i]) {
				childNodeStr += createNode(childNodeName, node.childNode[i][childNodeName])._resultName
			}
		}

	}
	if (name !== 'view') {
		nameArr.push(name)
	}
	return {
		_resultName: `<${name}${node.isComponent ? '' : ' class='+ `"${nodeName}"`}${node.slot ? ' slot=' + `"${node.slot}"` : ' '}${' ' + props}>${childNodeStr}</${name}>`,
		_componentsName: nameArr,
	}

}

/**
 *  需要修改放在组件内部的文件夹
 * @param {type} file   当前图片
 */
function copyStaticFile(file, dirName) {
	let filePath = path.join(__dirname, file)
	let targetFilePath = path.join(__dirname, `./app/${dirName}/`, file)
	fs.copyFileSync(filePath, targetFilePath)
}

/**
 * @param {type} dirName   包名
 *  @param {type} i   索引
 */
function readPagesJson(dirName, i) {
	let jsonData = fs.readFileSync(path.join(__dirname, `./app/${dirName}/pages.json`), {
		encoding: 'utf8'
	})
	pageArr[i].pagesJson = JSON.parse(jsonData)
}

/**
 * 递归生成当前页面prop数据放入data
 * @param {String} nodeName 节点名称（key）
 * @param {String} node 节点（value）
 * @param {String} _componentsName 查找子appoint字段并拼接好组件名需要递归去生成
 */
function getImportComponent(nodeName, node) {
	let arr = []
	if (node.isComponent) {
		arr.push(_componentsName)
	}
	if (node.childNode) {
		for (let childNodeName in node.childNode) {
			arr = [...arr, ...getImportComponent(childNodeName, node.childNode[childNodeName])]
		}
	}
	return arr
}

/**
 *  检测用户是否指定组件或者是随机组件
 * @param {*} nodeName 
 */
function checkoutAppoint(nodeName, node) {
	if ((node.appoint instanceof Array && node.appoint.length <= 0) || node.appoint <= 0) {
		throw new Error(`请填写随机组件数组或者指定组件序号！，组件名为${nodeName}`);
		return
	}
	if ((node.appoint instanceof Array && node.appoint.length >= 1)) {
		node.appoint.forEach(__ => {
			if (__ == 0) {
				throw new Error(`随机组件不能为0，组件名为${nodeName}`);
			}
		})
	}
	let componentsName = {
		numbers: '',
		name: ''
	}
	if (node.appoint instanceof Array && node.appoint.length > 0) {
		let appoint = node.appoint[randomNum(0, node.appoint.length - 1)]
		componentsName.name = `${nodeName}${appoint}`
		componentsName.numbers = appoint
		let componentsFileName = path.join(__dirname, `/components/${componentsName.name}`)
		fs.access(componentsFileName, (err) => {
			if (err) {
				throw new Error(`请添加组件名为${name}的组件，放在components目录下！`)
			}

		});
	} else {
		componentsName.name = `${nodeName}${node.appoint > 0 ? node.appoint : ''}`
		if (typeof node.appoint == 'string') {
			throw new Error('\x1B[31m', `组件${nodeName}${node.appoint}当中的appoint必须为数字！`)
		}
	}

	return componentsName
}

/**
 * @param {string} component 组件名
 * @param {string} dirName app包名
 */
function copyComponent(component, dirName) {
	let originFilePath = path.join(__dirname, `./components/${component}`)
	let toFilePath = path.join(__dirname, `./app/${dirName}/pages/components/${component}`)
	const copyFile = (_originFilePath, _toFilePath, flag) => {
		fs.access(_toFilePath, (err) => {
			if (err) {
				fs.mkdirSync(_toFilePath, {
					recursive: true
				});
			}
		});
		fs.readdir(_originFilePath, (err, Filelist) => {
			if (err) {
				if (flag == 0) { // 拷贝文件非图片
					throw new Error(`请检查components下是否存在该组件，组件名为${component}`);
				}
				if (flag == 1) {
					throw new Error(`请检查static下是否存在该组件图片文件夹，组件名为${component}`);
				}
			}
			// console.log('Filelist',Filelist);
			Filelist.forEach((item, index) => {
				const ss = path.resolve(_originFilePath, item);
				fs.stat(ss, (err, stat) => {
					if (err) {
						throw new Error(`请检查组件文件名是否正确，组件名为${component}`);
					}
					const c_OriginFilePath = path.resolve(_originFilePath, item);
					const c_ToFilePath = path.resolve(_toFilePath, item);
					if (stat.isFile()) {
						fs.createReadStream(c_OriginFilePath).pipe(fs.createWriteStream(
							c_ToFilePath));
					}
					let o_Path = path.join(__dirname, `./static/${component}`)
					let o_toFilePath = path.join(__dirname,
						`./app/${dirName}/static/${component}`)
					fs.access(o_toFilePath, (err) => {
						if (err) {
							// fs.mkdirSync(o_toFilePath);
							copyFile(o_Path, o_toFilePath, 1)
						}
					});
				});
			});
		});
	}
	copyFile(originFilePath, toFilePath, 0)
};

function titleCase5(str) {
	return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase());
}

/**
 * @param {string} dirName 包名
 * @param {string} index 当前第几个页面
 */
function writePageJson(dirName, index) {
	let pagesJsonData = pageArr[index].pagesJson
	fs.readdir(path.join(__dirname, `./app/${dirName}/pages`), (err, files) => {
		let arr = []
		files.forEach(e => {
			if (e == 'homePage') {
				arr = [{
					"path": `pages/${e}/${e}`,
					"style": {
						"navigationStyle": "custom"
					}

				}, ...arr]
			} else if (e != 'components' && e != '.DS_Store' && !e.includes('.css') && !e.includes(
					'.js')) {
				arr.push({
					"path": `pages/${e}/${e}`,
					"style": {
						"navigationStyle": "custom"
					}
				})
			}

		})
		pagesJsonData.pages = arr
		fs.writeFile(path.join(__dirname, `./app/${dirName}/pages.json`), JSON.stringify(pagesJsonData), (
			err) => {
			console.log('\x1B[36m%s\x1B[0m', `****************生成app成功****************`);
			console.log('\x1B[36m%s\x1B[0m', `请将app目录下的 ${dirName} 文件拖到HBuilderX一级目录下运行`);
			console.log('\x1B[36m%s\x1B[0m', `请将app目录下的 ${dirName} 文件拖到HBuilderX一级目录下运行`);
			console.log('\x1B[36m%s\x1B[0m', `请将app目录下的 ${dirName} 文件拖到HBuilderX一级目录下运行`);
			console.log('\x1B[36m%s\x1B[0m', `****************生成app成功****************`);
		})
	})
}

/**
 * @param {string} minNum 最小的数
 * @param {string} maxNum 最大的数
 */
function randomNum(minNum, maxNum) {
	const num = Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum
	return num
}

function start() {
	readJson()
}
start()